package io.chenhd.gateway.outbound.netty4;

import io.chenhd.gateway.cache.ConnectCache;
import io.chenhd.gateway.client.NettyHttpClient;
import io.chenhd.gateway.config.RouterConfig;
import io.chenhd.gateway.router.HttpEndpointRouter;
import io.chenhd.gateway.cache.ContextCache;
import io.chenhd.gateway.threadpool.ThreadPool;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class NettyHttpInboundHandler extends ChannelInboundHandlerAdapter {

    private List<String> backendUrls;
    private HttpEndpointRouter router;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        FullHttpRequest fullRequest = (FullHttpRequest) msg;
        String requestUri = fullRequest.uri();
        String backendUrl = RouterConfig.getBackendUrl(requestUri);
        if (backendUrl == null) {
            handleResponseNotFound(ctx);
        }

        final String url = backendUrl + fullRequest.uri();
        ThreadPool.threadPoolExecutor.submit(() -> {
            try {
                FullHttpResponse response = httpGet(ctx, url);
                handleResponse(fullRequest, ctx, response);
            } catch (Exception e) {
                e.printStackTrace();
                //log error
            }
        });
    }

    private FullHttpResponse httpGet(ChannelHandlerContext ctx, String url) throws Exception {
        FullHttpResponse response = null;
        int retry = 0;
        while (retry++ <= 3) {
            try {
                Channel clientChannel = NettyHttpClient.connect(ctx, url);
                synchronized (clientChannel) {
                    if ((response = ContextCache.responseCache.get(ContextCache.clientCache.get(ctx))) == null) {
                        //等待唤醒
                        clientChannel.wait(5000);
                    }
                }
                ConnectCache.releaseChannel(url, clientChannel);
                if (response.status().code() == 200) {
                    System.out.println("netty request success to " + url);
                    return response;
                }
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
                //error log
            }
        }
        return response;
    }

    private void handleResponse(final FullHttpRequest fullRequest, final ChannelHandlerContext ctx, final FullHttpResponse endpointResponse) throws Exception {
        if (fullRequest != null && endpointResponse != null) {
            if (!HttpUtil.isKeepAlive(fullRequest)) {
                ctx.write(endpointResponse).addListener(ChannelFutureListener.CLOSE);
            } else {
                ctx.write(endpointResponse);
            }
        } else {
            String content = "system error";
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(content.getBytes()));
            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", content.length());
            ctx.write(response);
        }
        ctx.flush();
//        ctx.close();
    }

    private void handleResponseNotFound(final ChannelHandlerContext ctx) throws Exception {
        String content = "url error";
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.wrappedBuffer(content.getBytes()));
        response.headers().set("Content-Type", "application/json");
        response.headers().setInt("Content-Length", content.length());
        ctx.write(response);
        ctx.flush();
//        ctx.close();
    }
}
